######################################################################
# Core Dispatcher
######################################################################
package EP::EPC::Core::Dispatcher;
use strict;

use EP::Config;

use EP::EPC::Core;
use EP::EPC::Core::User_Management;
use EP::EPC::Core::Data_Selection;
use EP::EPC::Core::Data_Transformation;
use EP::EPC::Core::Between_Group_Analysis;
use EP::EPC::Core::Ordination;
use EP::EPC::Core::Clustering_Comparison;
use EP::EPC::Core::Advanced_Clustering_Comparison;
use EP::EPC::Core::Clustering;
use EP::EPC::Core::Signature_Algorithm;
use EP::EPC::Core::User_Preferences;
use EP::EPC::Core::Data_Management;
use EP::EPC::Core::Data_Viewer;
use EP::EPC::Core::Experiment_Design;
use EP::EPC::Core::GO_Annotation;
use EP::EPC::Core::Sequence_Homology;
use EP::EPC::Core::Sequence_Motif;
use EP::EPC::Core::Sequence_Management;
use EP::EPC::Core::Pattern_Matching;
use EP::EPC::Core::Threeway_Similarity_Analysis;
use EP::EPC::Core::Statistics;
use EP::EPC::Core::Chromosome_Localization;
use EP::EPC::Core::Treemap_View;
use EP::EPC::Core::Data_Normalization;
use EP::EPC::Core::Sequence_Logo;

use EP::EPC::Core::Upload;
use EP::Common::DBI::Session;

use EP::Common;
use EP::Common::Error qw ( :try );

use XML::LibXML;

=head1 NAME

EP::EPC::Core::Dispatcher - core modules dispatcher, organized by core
component ID

=head1 SYNOPSIS

TBD

=head1 DESCRIPTION

=cut

sub new
    {
    my $proto = shift;
    my ( $query ) = @_;
    my $class = ref($proto) || $proto;
    my $self  = {};
    bless $self, $class;

    $self->initialize ( $query );
    return $self;
    }

sub initialize
    {
    my $self = shift;
    my ($query) = @_;

    $self->{query} = $query;
    my $session_id = $query->param ("session_id");

    throw EP::Common::Error( -value => CORE_CALL_NO_SESSION_ID ) if not $session_id;

    $self->{session} = EP::Common::DBI::Session->retrieve($session_id);
    die "Admin error: invalid session id passed to EP. Check that your epc_targets.xml is pointing where it should be." if not $self->{session};

    $self->{request_user_agent} = $query->param("request_user_agent");

    $self->{process_component_id} = $self->{query}->param("process_component_id");

    try {
        my $process_comp_id = $self->{query}->param ( "process_component_id" );

        #### HACK ####
        # Only initialize that component that actually needs to be processed.....
        # needs to go to database or other type of configuration file
        $self->{0}  = new EP::EPC::Core::User_Management( $query, $self, 0 )                    if $self->{query}->param("process_component_id") == 0;
        $self->{2}  = new EP::EPC::Core::Data_Selection( $query, $self, 2 )                     if $self->{query}->param("process_component_id") == 2;
        $self->{3}  = new EP::EPC::Core::Clustering( $query, $self, 3 )                         if $self->{query}->param("process_component_id") == 3;
        $self->{4}  = new EP::EPC::Core::Clustering( $query, $self, 4 )                         if $self->{query}->param("process_component_id") == 4;
        $self->{6}  = new EP::EPC::Core::Between_Group_Analysis( $query, $self, 6 )             if $self->{query}->param("process_component_id") == 6;
        $self->{7}  = new EP::EPC::Core::Ordination( $query, $self, 7 )                         if $self->{query}->param("process_component_id") == 7;
        $self->{8}  = new EP::EPC::Core::Data_Transformation( $query, $self, 8 )                if $self->{query}->param("process_component_id") == 8;
        $self->{9}  = new EP::EPC::Core::Clustering_Comparison( $query, $self, 9 )              if $self->{query}->param("process_component_id") == 9;
        $self->{10} = new EP::EPC::Core::Upload( $query, $self, 10 )                            if $self->{query}->param("process_component_id") == 10;
        $self->{12} = new EP::EPC::Core::Signature_Algorithm( $query, $self, 12 )               if $self->{query}->param("process_component_id") == 12;
        $self->{13} = new EP::EPC::Core::Clustering( $query, $self, 13 )                        if $self->{query}->param("process_component_id") == 13;
        $self->{14} = new EP::EPC::Core::Sequence_Motif( $query, $self, 14 )                    if $self->{query}->param("process_component_id") == 14;
        $self->{15} = new EP::EPC::Core::Transient_Component( $query, $self, 15 )               if $self->{query}->param("process_component_id") == 15;
        $self->{16} = new EP::EPC::Core::Pattern_Matching( $query, $self, 16 )                  if $self->{query}->param("process_component_id") == 16;
        $self->{17} = new EP::EPC::Core::User_Preferences( $query, $self, 17 )                  if $self->{query}->param("process_component_id") == 17;
        $self->{18} = new EP::EPC::Core::Data_Management( $query, $self, 18 )                   if $self->{query}->param("process_component_id") == 18;
        $self->{22} = new EP::EPC::Core::Data_Viewer( $query, $self, 22 )                       if $self->{query}->param("process_component_id") == 22;
        $self->{24} = new EP::EPC::Core::Data_Transformation( $query, $self, 24 )               if $self->{query}->param("process_component_id") == 24;
        $self->{25} = new EP::EPC::Core::Experiment_Design( $query, $self, 25 )                 if $self->{query}->param("process_component_id") == 25;
        $self->{26} = new EP::EPC::Core::GO_Annotation( $query, $self, 26 )                     if $self->{query}->param("process_component_id") == 26;
        $self->{27} = new EP::EPC::Core::Sequence_Homology( $query, $self, 27 )                 if $self->{query}->param("process_component_id") == 27;
        $self->{100}= new EP::EPC::Core::Upload( $query, $self, 100 )                           if $self->{query}->param("process_component_id") == 100;
        $self->{101} = new EP::EPC::Core::Upload( $query, $self, 101 )                          if $self->{query}->param("process_component_id") == 101;
#       $self->{102} = new EP::EPC::Core::Threeway_Similarity_Analysis( $query, $self, 102 )    if $self->{query}->param("process_component_id") == 102;
        $self->{103} = new EP::EPC::Core::Sequence_Management( $query, $self, 103 )             if $self->{query}->param("process_component_id") == 103;
        $self->{104} = new EP::EPC::Core::Statistics( $query, $self, 104 )                      if $self->{query}->param("process_component_id") == 104;
        $self->{105} = new EP::EPC::Core::Advanced_Clustering_Comparison( $query, $self, 105 )  if $self->{query}->param("process_component_id") == 105;
        $self->{106} = new EP::EPC::Core::Chromosome_Localization( $query, $self, 106 )         if $self->{query}->param("process_component_id") == 106;
        $self->{111} = new EP::EPC::Core::Treemap_View( $query, $self, 111 )                    if $self->{query}->param("process_component_id") == 111;
        $self->{112} = new EP::EPC::Core::Data_Normalization( $query, $self, 112 )              if $self->{query}->param("process_component_id") == 112;
        $self->{113} = new EP::EPC::Core::Sequence_Logo( $query, $self, 113 )                   if $self->{query}->param("process_component_id") == 113;
        }
    catch EP::Common::Error with
        {
        my $e = shift;
        $self->dispatcher_error ( $e );
        }
    otherwise
        {
        my $e = shift;
        $self->dispatcher_error ( $e );
        }
    }

sub dispatch_request
    {
    my $self = shift;

    try {
        my $process_comp_id = $self->{query}->param("process_component_id");

        if ( not defined $process_comp_id or not exists $self->{$process_comp_id} )
            {
            throw EP::Common::Error ( -value => CD_UNKNOWN_COMPONENT_ID );
            }

        my $new_action = $self->{$process_comp_id}->process_component();
        die "Component processing returned no new action!" if not $new_action;

        $self->{resultant_action} = $new_action if ref($new_action) eq "EP::Common::DBI::Action";
        }
    catch EP::Common::Error with
        {
        my $e = shift;
        $self->dispatcher_error($e);
        }
    catch Error::Simple with
        {
        # this handles simple 'die' statements - these are wrapped by Error.pm's try-catch into Error::Simple, and we want
        # to recast them as EP errors, keeping file/line info.
        my $e = shift;
        $self->dispatcher_error( new EP::Common::Error(%{$e}) );
        }
    otherwise
        {
        my $e = shift;
        warn "Unhandled error $e";
        $self->{error} = new EP::Common::Error( -text => "Unhandled error: $e" );
        }
    }

sub dispatcher_error
    {
    my $self = shift;
    my ($error) = @_;

    chomp ( my $errtxt = $error->{-text} );
    warn "$errtxt in $error->{-file} at $error->{-line}\n" if DEBUG;

    $self->{error} = $error if defined $error;

    return $error;
    }

sub cgi_output
    {
    my $self = shift;

    print $self->{query}->header( -type => "text/xml" );
    print $self->result_content();

    $self->{session}->status("DONE");
    }

sub error_message
    {
    my $self = shift;
    return "<error err_code=\"".$self->{error}->value()."\" severity=\"".$self->{error}->severity()."\">$self->{error}</error>\n";
    }

sub result_content
    {
    my $self = shift;

    my $doc = XML::LibXML->createDocument();
    my $root = $doc->createElement("epc_output");
    $doc->setDocumentElement($root);

    # errors
    if ( $self->{error} )
        {
        my $error = $doc->createElement("error");
        $error->setAttribute( "err_code", $self->{error}->value() );
        $error->setAttribute( "severity", $self->{error}->severity() );

        chomp( my $err_text = "$self->{error}" );
        $error->appendChild( $doc->createTextNode($err_text) );
        $root->appendChild($error);
        }

    return $doc->toString() if not exists $self->{resultant_action};

    $root->setAttribute( "action_id", $self->{resultant_action}->id );
    return $doc->toString();
    }

1;
